package com.knightcloud.micro.auth.config.security;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.knightcloud.micro.auth.config.security.jackson.LoginUserDeserializer;
import com.knightcloud.micro.upms.api.dto.SysUserDto;
import lombok.Data;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.io.Serial;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 登录用户信息
 *
 * @author knight
 */
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonDeserialize(using = LoginUserDeserializer.class)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
		isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
public class LoginUser implements UserDetails, CredentialsContainer {

	@Serial
	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

	/**
	 * 用户id
	 */
	private String id;

	/**
	 * 用户名
	 */
	private String username;

	/**
	 * 密码
	 */
	private String password;

	/**
	 * 头像
	 */
	private String avatar;

	/**
	 * 电话
	 */
	private String phone;

	/**
	 * 邮箱
	 */
	private String email;

	/**
	 * 性别
	 */
	private Integer gender;

	/**
	 * 出生日期
	 */
	@JsonSerialize(using = LocalDateSerializer.class)
	private LocalDate birth;

	/**
	 * 权限列表
	 */
	private Set<GrantedAuthority> authorities;

	/**
	 * 是否启用
	 */
	private boolean enabled;

	/**
	 * 是否未过期
	 */
	private boolean accountNonExpired;

	/**
	 * 是否非锁定账户
	 */
	private boolean accountNonLocked;

	/**
	 * 凭证是否未过期
	 */
	private boolean credentialsNonExpired;

	/**
	 * 登录用户
	 * @param sysUser 系统用户dto
	 */
	public LoginUser(SysUserDto sysUser) {
		this.id = sysUser.getId();
		this.username = sysUser.getUsername();
		this.password = sysUser.getPassword();
		this.avatar = sysUser.getAvatar();
		this.phone = sysUser.getPhone();
		this.email = sysUser.getEmail();
		this.gender = sysUser.getGender();
		this.birth = sysUser.getBirth();
		this.accountNonExpired = !sysUser.getExpired();
		this.accountNonLocked = !sysUser.getLocked();
		this.credentialsNonExpired = !sysUser.getPasswordExpired();
		this.enabled = sysUser.getEnable();
		addGrantedAuthority(sysUser.getRoleSet(), sysUser.getPermSet());
	}

	public LoginUser(String id, String username, String password, String avatar, String phone, String email,
			Integer gender, LocalDate birth, Set<? extends GrantedAuthority> authorities, boolean enabled,
			boolean accountNonExpired, boolean accountNonLocked, boolean credentialsNonExpired) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.avatar = avatar;
		this.phone = phone;
		this.email = email;
		this.gender = gender;
		this.birth = birth;
		this.authorities = authorities.stream()
			.sorted(Comparator.comparing(GrantedAuthority::getAuthority))
			.collect(Collectors.toCollection(LinkedHashSet::new));
		this.enabled = enabled;
		this.accountNonExpired = accountNonExpired;
		this.accountNonLocked = accountNonLocked;
		this.credentialsNonExpired = credentialsNonExpired;
	}

	/**
	 * 添加授权 为什么要排序？
	 * 参考：<a href="https://github.com/spring-projects/spring-security/issues/981">...</a>
	 * @param roleSet 角色组
	 * @param permSet 权限组
	 */
	public void addGrantedAuthority(Set<String> roleSet, Set<String> permSet) {
		Stream<GrantedAuthority> roleStream = roleSet.stream().map(SimpleGrantedAuthority::new);
		Stream<GrantedAuthority> permStream = permSet.stream().map(SimpleGrantedAuthority::new);
		this.authorities = Stream.concat(roleStream, permStream)
			.sorted(Comparator.comparing(GrantedAuthority::getAuthority))
			.collect(Collectors.toCollection(LinkedHashSet::new));
	}

	/**
	 * 认证完成后删除凭证
	 */
	@Override
	public void eraseCredentials() {
		this.password = null;
	}

}
